{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}

(**************************************************************

Ce que le programme doit pouvoir faire:
- Extraire les données sur les pages web
- Gérer une liste d'action

La liste d'action doit pouvoir enchainer les différentes
options:
- Construire un batiment
- Effectuer une recherche
- Ajuster les pourcentages selon X, Y, Z choisit
- Ajuster les pourcentages X Y Z pour pouvoir effectuer
  le plus vite possible l'action suivante


Le tout avec des statistiques de prévisions pour que
le programmeur des actions puisse savoir environ
le nombre de ressource qu'il aura

Pour obtenir la capacité maxi pour chaque "batiment", on
met tout à 100% et on utilise le facteur de production.


**************************************************************)
unit modBaseType;

interface


uses ESBDates, sFonctionsChaines, SysUtils;

const
   RESSOURCE_ID_METAL     = 1;
   RESSOURCE_ID_CRISTAL   = 2;
   RESSOURCE_ID_DEUTERIUM = 3;

const
    CNT_ERROR_USER           = -1;
    CNT_ERROR_FATAL          = -8;   // Cette erreur implique l'arrêt du robot
    CNT_ERROR_SHOULD_RELOGIN = -7;   // plus utilisé car à présent on sait si il faut ou pas se relogger

    // Erreurs certaines:
    CNT_ERROR_MUST_RELOGIN   = -9;  // La page affiche une erreur de DB Problem
    CNT_ERROR_SOCKET         = -10; // Erreur de socket (impossible de se connecter)
    CNT_ERROR_PARSING        = -11; // Erreur de parsing (erreur fatale)

    CNT_SUCCESS              = 1;
    CNT_NO_EFFECT            = 0;

 const
     CNT_LOGIN_ERROR = CNT_ERROR_USER;
     CNT_LOGIN_ERROR_URLFROMFORMULAIREINVALID = -21;
     CNT_LOGIN_ERROR_DLFORMULAIRE_ECHEC       = -22;
     CNT_LOGIN_ERROR_SOCKETNONPRET            = -23;
     CNT_LOGIN_ERROR_DEJAENCOURS              = -24;
     CNT_LOGIN_ERROR_AUTRE_ACTION_ENCOURS     = -25;



    CNT_CONSTRUCTION_DISPO_NEVER_WITHTHISPROD = -500;

    
type
    TBasicActionResult = (basERREUR_FATALE,
                          basERREUR_PARSING,
                          basERREUR_SOCKET,

                          basMUST_RELOGIN,
                          basAUTRE_CONSTRUCTION_ENCOURS,
                          basGID_NONDISPONIBLE,
                          basGID_NEEDMORERESSOURCE,
                          basACTION_EFFECTUE);

    TOGOrdre = (ooNeant, ooAttaquer, ooTransport, ooExploiter, ooEspionner, ooBaser, ooColoniser);
    TOGDirection = (odNeant, odRetour, odAller);

    TOGDestinationType = (ctNeant, ctPlanete, ctRuines, ctLune);

const
    CNT_POSITION_LUNE        = 100;
    CNT_POSITION_DEBRIS      = 200;

type

    TPlanetLocation = record    // pour 1:2:3 on a:
       Galaxie      : integer;        //   1
       SystemSolaire: integer;        //   2
       Position     : integer;        //   3
    end;



    TPlanetInfo = record
       Location: TPlanetLocation;
       Nom     : string;
    end;

    TPlanetLinkInfo = record
       Location: TPlanetLocation;
       Lien    : string;
    end;

    TOGRessource = record
        Metal    : integer;
        Cristal  : integer;
        Deuterium: integer;
    end;

    TOGRessourceAll = record
        Metal : integer;
        Cristal : integer;
        Deuterium: integer;
        Energie : integer;
    end;

    
    // Fonction pour obtenir la capacite de fret requise pour tout prendre
    TProcGetFretCapaciteRequise = function (Ressource: TOGRessourceAll): integer;


    // Pour les stats qui apparaissent en haut de page à chaque fois
    TOGRessPow = record
        Production: TOGRessourceAll;
        EnergieRestante: integer;
    end;


    // Suivit de ses propres flottes (modFlotteTracking)
    TFlotteOwnUID = record
       // la date de départ ne CHANGE jamais quelque soit le sens de la flotte
       DateDepart: int64;

       // Par contre la date de RETOUR peut changer, et on ne PEUT PAS s'en servir
       // comme UID
       // Partant du principe qu'il faut au moins 1 seconde pour envoyer une flotte
       // La date de départ suffit LARGEMENT pour suivre une flotte au cours du temps

       // A cela, on complète de la location Depart/Arrivee
       // Quelque soit le sens, ces coordonnées ne change pas, lors du retour
       // les coord de départ sont les même que lors de l'aller
       Location  : int64;
    end;
    PFlotteOwnUID = ^TFlotteOwnUID;


    TFlotteEnvoiData = record
       // cible où envoyer la flotte
       Cible    : TPlanetLocation;

       // type de la cible (planète/lune/champ de ruine)
       CibleType: TOGDestinationType;

       // Vitesse déjà entre 1 et 10 compris
       Vitesse: integer;

       // Ordre
       Ordre: TOGOrdre;

       // Ressources à transporter
       Ressources: TOGRessourceAll;
    end;
    PFlotteEnvoiData = ^TFlotteEnvoiData;


    TFlotteReceptionData = record
       // Cible: self;
       // CibleType: self, la lune étant une planète à part
       // Vitesse: inutile de connaitre, seule la date d'impact (ou durée) est importante

       // Ordre nécessaire pour savoir si la flotte va rester à quoi ou non
       // ou même si c'est une attaque ou non
       Ordre: TOGOrdre;

       // Ressources transportées
       Ressources: TOGRessourceAll;
    end;
    PFlotteReceptionData = ^TFlotteReceptionData;




    TOGResUsedIt = record
        IdOpt     : string[32];
        Production: TOGRessourceAll;
    end;


const
    CNT_RESUSED_GID_REVENU_BASE     = 99870;
    CNT_RESUSED_GID_REVENU_BASE_STR = 'Revenu de base';


type

    // Pour la page de gestion des ressources, suite au parsing de la page
    // on obtient un tableau de ce type de record qui représente très bien
    // la page et les données qu'elle contient
    TOGResUsed = record
        // Nom de l'option dans le formulaire:
        // Mettre Activite à -1 si aucun
        IdOpt  : string[32];

        // Element concerné (batiment ou satellite solaire) (depuis l'update Juin 2007)
        Element: string;
        ElementGID: integer;
        ElementLVL: integer;    // niveau du batiment ou nombre de satellite
        
        // Positif si en produit, négatif si en consomme
        prMetal    : integer;
        prCristal  : integer;
        prDeuterium: integer;

        // Centrale, production d'énergie, positif (sinon 0)
        prEnergie: integer;

        // Autre batiment, consomation d'énergie, nombre positif:
        csEnergieUsed : integer;
        csEnergieAllow: integer;

        // Pourcentage d'activite, 0, 10, ... 100
        // -1 si l'activité ne peut pas être définit
        Activite: integer;

        ActiviteForOptimization: integer;
    end;
    POGResUsed = ^TOGResUsed;


    TTypeGID = (tgidBatiment, tgidRecherche, tgidVaisseau, tgidDefense, tgidAutre);



const
    CNT_ITEM_DESIGN_DEFAUT    = 0;
    CNT_ITEM_DESIGN_BATIMENT  = 1;
    CNT_ITEM_DESIGN_RECHERCHE = 2;
    CNT_ITEM_DESIGN_VAISSEAUX = 3;
    CNT_ITEM_DESIGN_DEFENSE   = 4;
    CNT_ITEM_DESIGN_RESPROD   = 5;


const
    MAX_GID_ARRAY = 510;
    MIN_GID_ARRAY = 1;


type
    // Pour la construction d'un bat ou la recherche d'une technologie 
    TOGItemCost = record
        ////////////////////////////////////////////////
        // Options extraites de la page web
        ////////////////////////////////////////////////

        // id de l'élement
        GID : integer;

        // Niveau du batiment, ou nombre d'unité pour la défense/vaisseaux
        Niveau: integer;

        // Prix de l'élément
        Prix: TOGRessourceAll;

        // Durée de construction/recherche en secondes
        Duree: integer;


        // expérimental, car la variable JavaScript peut changer
        // Cela correspond à dans combien de temps cela se termine (pas à une date unix)
        ConstructionFinDans: integer;

        // Lien (relatif) pour lancer la construction
        // Vide si on ne peut pas construire/rechercher cet élément
        // Ou
        // Nom de l'input box si c'est un formulaire
        Link: string[255];

        // Ne sert uniquement lorsque l'on joue à travers l'interface du prog
        Nom : string[255];

        // Permet de savoir si l'élément est en cours de construction
        EnConstruction: boolean;


        ////////////////////////////////////////////////
        // Options supplémentaires servant à la gestion de l'élément
        ////////////////////////////////////////////////

        // Dans combien de temps on aura assez de ressource
        // pour construire l'élément -1 si indéfinit, 0 si
        // déjà constructible
        CanBeBuildIn   : integer;

        // Même chose, mais lorsque la production est optimisée pour
        // construire cet élément
        CanBeBuildInOpt: integer;


        // Type du GID (bat, recherche, vaisseau, def)
        // es-ce vraiment utile de le stocker puisqu'on peut le récupérer facilement ? compromis entre temps et espace
        GIDType: TTypeGID;



        LigneInGrid: integer;  // Permet une mise à jour sans effacer toutes les lignes


        ///////////////////////////////////////////////
        // Pour la simulation avancée
        ///////////////////////////////////////////////

        // Date à laquelle la construction a été lancé
        // Grace au champ Duree on peut connaitre la date de fin
        ConstructionStartAt: int64;
    end;
    POGItemCost = ^TOGItemCost;


    ///////////////////////////////////////////////

    TNiveauStats = record
       // Numero du niveau
       Numero     : integer;

       // quantité produite
       Production : TOGRessourceAll;

       // consomme général de l'énergie, mais aussi du deut pour les centrale à fusion
       // les valeurs sont obligatoirement négative
       Consomation: TOGRessourceAll;
    end;

    TOGNiveauInfos = array[0..255] of TNiveauStats;


const
    CNT_OG_BAT_METAL    = 'Mine de métal';
    CNT_OG_BAT_CRISTAL  = 'Mine de cristal';
    CNT_OG_BAT_DEUT     = 'Synthétiseur de deutérium';
    CNT_OG_BAT_CSOLAIRE = 'Centrale électrique solaire';
    CNT_OG_BAT_CFUSION  = 'Centrale électrique de fusion';
    CNT_OG_BAT_UROBOTS  = 'Usine de robots';
    CNT_OG_BAT_UNANITES = 'Usine de nanites';
    CNT_OG_BAT_CSPATIAL = 'Chantier spatial';
    CNT_OG_BAT_HMETAL   = 'Hangar de métal';
    CNT_OG_BAT_HCRISTAL = 'Hangar de cristal';
    CNT_OG_BAT_HDEUT    = 'Réservoir de deutérium';
    CNT_OG_BAT_LAB      = 'Laboratoire de recherche';
    CNT_OG_BAT_SILO     = 'Silo à missile';

    


    // Comparaison de location
    function CompareLocationMoonAutoAjust(l1, l2: TPlanetLocation): boolean; inline;
    function CompareLocation(l1, l2: TPlanetLocation): boolean; inline;

    // pour trier les locations avec FastSort
    function trieCompareLocation(l1, l2: TPlanetLocation): integer;


    procedure mbtIncLocation(var Location: TPlanetLocation);

    function mbtIsLune(Coord: TPlanetLocation): boolean; overload;
    function mbtIsLune(Position: integer): boolean; overload;
    function mbtGetRealPosition(Position: integer): integer;


    // Pour obtenir le type d'un élément
    function GetTypeOfGID(Gid: integer): integer;
    function GetTypeOfTGID(Gid: integer): TTypeGID;






    function OGDATETIME_StrToDate(aDate : String; iYear: integer = 0) : TDateTime;
    function OGDATETIME_StrToUnix(aDate : String; iYear: integer = 0) : int64;


    function mbtCibleTypeToStr(t: TOGDestinationType): string;


    // Définit si un champ de ruine est vide ou pas
    function mbtRessourceCdRNonVide(r: TOGRessourceAll): boolean;


    // convertie une location en string
    function mbtPlanetLocationToStr(l: TPlanetLocation): string;



    // Ajoute des elements
    function mbtAddPrix(p1, p2: TOGRessourceAll; NFoisP2: integer = 1): TOGRessourceAll;



    function mbtIsValidGID(Gid: integer): boolean;

    (*
<SCRIPT language=JavaScript>
pp="388";pk="21";pl="164992";ps="21d016772ef";t();
</script>
*)

implementation

uses Math, modGidToStr;


function mbtRessourceCdRNonVide(r: TOGRessourceAll): boolean;
begin
   // vide par défaut
   Result := false;

   if (r.Metal > 0) or (r.Cristal > 0) then begin
      Result := true;
   end;

end;

function mbtIsValidGID(Gid: integer): boolean;
begin
    Result := false;
    if Gid < MIN_GID_ARRAY then exit;
    if Gid > MAX_GID_ARRAY then exit;

    Result := true;
end;


function mbtPlanetLocationToStr(l: TPlanetLocation): string;
begin
   Result := format('%d:%d:%d', [l.Galaxie, l.SystemSolaire, l.Position]);
end;


function mbtCibleTypeToStr(t: TOGDestinationType): string;
begin
   case t of
      ctNeant  : Result := 'Neant';
      ctPlanete: Result := 'Planete';
      ctRuines : Result := 'Ruines';
      ctLune   : Result := 'Lune';
   end;
end;


function mbtGetRealPosition(Position: integer): integer;
begin
   if mbtIsLune(Position) then begin
      Result := Position - CNT_POSITION_LUNE;
   end else begin
      Result := Position;
   end;
end;


function mbtIsLune(Coord: TPlanetLocation): boolean;
begin
   Result := mbtIsLune(Coord.Position);
end;


function mbtIsLune(Position: integer): boolean;
begin
   if Position > CNT_POSITION_LUNE then begin
      Result := true;
   end else begin
      Result := false;
   end;

end;

function mbtAddPrix(p1, p2: TOGRessourceAll; NFoisP2: integer = 1): TOGRessourceAll;
begin
   if NFoisP2 = 1 then begin
      Result.Metal     := p1.Metal     + p2.Metal;
      Result.Cristal   := p1.Cristal   + p2.Cristal;
      Result.Deuterium := p1.Deuterium + p2.Deuterium;
      Result.Energie   := p1.Energie   + p2.Energie;

   end else begin
      Result.Metal     := p1.Metal     + (NFoisP2 * p2.Metal);
      Result.Cristal   := p1.Cristal   + (NFoisP2 * p2.Cristal);
      Result.Deuterium := p1.Deuterium + (NFoisP2 * p2.Deuterium);
      Result.Energie   := p1.Energie   + (NFoisP2 * p2.Energie);
   end;


end;


procedure mbtIncLocation(var Location: TPlanetLocation);
begin

    if Location.Position < 15 then
       Location.Position := Location.Position + 1

    else if Location.SystemSolaire < 499 then begin
       // Location.Position = 15
       Location.SystemSolaire := Location.SystemSolaire + 1;
       Location.Position := 1;

    end else begin
       Location.Position := 1;
       Location.SystemSolaire := 1;
       Location.Galaxie := Location.Galaxie + 1;

    end;


end;


const
   OGDATETIME_StrWeekDay : String = 'MonTueWedThuFriSatSun';
   OGDATETIME_StrMonth   : String = 'JanFebMarAprMayJunJulAugSepOctNovDec';

function OGDATETIME_StrToUnix(aDate : String; iYear: integer = 0) : int64;
begin
    Result := DateTimeToUnix(OGDATETIME_StrToDate(aDate, iYear));
end;


function OGDATETIME_StrToDate(aDate : String; iYear: integer = 0) : TDateTime;
var Year, Month, Day : Word;
    Hour, Min,   Sec : Word;
    //xGMTDiff : integer;
    //xPos: integer;
    //s: string;
begin
    { Thu Nov 24 16:28:36 }
    { Thu Nov 24 17:31:16 }
    { Thu Dec 1 0:18:10 }

    //123456789 123456789
    if length(aDate) < 16 then begin
       Result := 0.0;
       exit;
    end;

    Day    := sfcTokenGetInt(aDate, 3, ' ', -1);
    //Day    := StrToIntDef(Copy(aDate, 9, 2), 0);
    Month  := (Pos(Copy(aDate, 5, 3), OGDATETIME_StrMonth) + 2) div 3;


    if Day < 1 then begin
       Result := 0.0;
       exit;
    end;

    if iYear = 0 then begin
       // L'année est l'année actuelle
       Year := ThisYear;
    end else
       Year := iYear;
       

    System.Delete(aDate, 1, 10);
    aDate := trimleft(aDate);

    // 10:10:35
    Hour   := sfcTokenGetInt(aDate, 1, ':', 260);
    Min    := sfcTokenGetInt(aDate, 2, ':', 260);
    Sec    := sfcTokenGetInt(aDate, 3, ':', 260);


    if (Hour > 255) or (Min > 255) or (Sec > 255) then begin
       Result := 0.0;
       exit;
    end;

    Result := EncodeDate(Year, Month, Day);
    Result := Result + EncodeTime(Hour, Min, Sec, 0);


    // Les dates ne sont pas en GMT sur ogame mais bien à l'heure française
    //xGMTDiff := ESBDGetHeureDiffGMT;
    //Result := DateAdd(Result,  SecsPerHr * xGMTDiff, 's');
end;





function trieCompareLocation(l1, l2: TPlanetLocation): integer;
begin


    // Compare renvoie < 0 si Item1 est inférieur à Item2,
    //                   0 s'ils sont égaux
    //                 > 0 si Item1 est supérieur à Item2
    // Voir http://alphomega.developpez.com/TriTList/
    // pour savoir comment trier sur plusieurs critére


    // Ici on ne compare que deux entiers, donc:
    if l1.Galaxie < l2.Galaxie then
       // Item1 est inférieur à Item2
       Result := -1

    else if l1.Galaxie = l2.Galaxie then begin
       // Egalité, même galaxie
       if l1.SystemSolaire < l2.SystemSolaire then begin
          Result := -1

       end else if l1.SystemSolaire = l2.SystemSolaire then begin
          // égalite, même système solaire
          if l1.Position < l2.Position then
             Result := -1

          else if l1.Position = l2.Position then
             Result := 0

          else
             Result := 1;

       end else
          Result := 1;

    end else
       // Sinon Item1 est supérieur à Item2
       Result := 1;

end;

function CompareLocationMoonAutoAjust(l1, l2: TPlanetLocation): boolean;
var x, y: integer;
begin
   Result := False;

   if l1.Galaxie        <> l2.Galaxie then exit;
   if l1.SystemSolaire  <> l2.SystemSolaire then exit;


   x := mbtGetRealPosition(l1.Position);
   y := mbtGetRealPosition(l2.Position);
   if x <> y then begin
      // pas la même position
      exit;
   end;

   Result := true;
   
end;

function CompareLocation(l1, l2: TPlanetLocation): boolean;
begin
   Result := False;

   if l1.Galaxie        <> l2.Galaxie then exit;
   if l1.SystemSolaire  <> l2.SystemSolaire then exit;
   if l1.Position       <> l2.Position then exit;

   Result := true;
end;


// TTypeGID = (tgidBatiment, tgidRecherche, tgidVaisseau, tgidDefense, tgidAutre);
function GetTypeOfTGID(Gid: integer): TTypeGID;
begin

    // Pour le fond maintenant
    if GID < 100 then begin
       // de 1 à 99: Batiment
       Result := tgidBatiment;

    end else if GID < 200 then begin
       // de 100 à 199: Recherche
       Result := tgidRecherche;

    end else if GID < 300 then begin
       //de 200 à 299: Vaisseaux
       Result := tgidVaisseau;

    end else if GID < 500 then begin
       // de 400 à 499: Defense
       Result := tgidDefense;

    end else if GID < 600 then begin
       // de 500 à 599: Defense
       // Missiles interplanétaires/interception
       Result := tgidDefense;

    end else
       Result := tgidAutre;

end;


function GetTypeOfGID(Gid: integer): integer;
begin

    // Pour le fond maintenant
    if GID < 100 then begin
       // de 1 à 99: Batiment
       Result := CNT_ITEM_DESIGN_BATIMENT;

    end else if GID < 200 then begin
       // de 100 à 199: Recherche
       Result := CNT_ITEM_DESIGN_RECHERCHE;

    end else if GID < 300 then begin
       //de 200 à 299: Vaisseaux
       Result := CNT_ITEM_DESIGN_VAISSEAUX;

    end else if GID < 500 then begin
       // de 400 à 499: Defense
       Result := CNT_ITEM_DESIGN_DEFENSE;

    end else
       Result := CNT_ITEM_DESIGN_DEFAUT;

end;

end.

